<?php

require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
/**
 * SeleniumTest is base class for Selenium Tests
 * Extends PHPUnit_Extensions_SeleniumTestCase
 * Holds a collection of methods used by tests
 * 
 * @author Joona Somerkivi <joona.somerkivi@solinor.com>
 */
class Lib_SeleniumTestcase extends PHPUnit_Extensions_SeleniumTestCase
{
    //Timeout of every "wait" methods in seconds
    protected static $waitTimeout = 10;
    //Time to sleep between tries in "wait" methods
    protected static $waitSleep = 500000; //In microseconds, default ½ second

    protected static $browser = '*googlechrome';
    protected static $browserUrl = 'http://localhost/';
    protected static $testSpeed = 1;

    public function  __construct($name = NULL, array $data = array(), $dataName = '', array $browser = array())
    {
        $settings = parse_ini_file(dirname(__FILE__).'/settings.ini');
        if($settings)
        {
            self::$waitTimeout = isset($settings['WAIT_TIMEOUT'])
                                    ? (int)$settings['WAIT_TIMEOUT']
                                    : self::$waitTimeout;
            self::$waitSleep = isset($settings['WAIT_SLEEP'])
                                    ? (int)$settings['WAIT_SLEEP']
                                    : self::$waitSleep;
            self::$browser = isset($settings['USE_BROWSER'])
                                    ? $settings['USE_BROWSER']
                                    : self::$browser;
            self::$browserUrl = isset($settings['TEST_SERVER'])
                                    ? $settings['TEST_SERVER']
                                    : self::$browserUrl;
            self::$testSpeed = isset($settings['TEST_SPEED'])
                                    ? (int)$settings['TEST_SPEED']
                                    : self::$testSpeed;
        }
        parent::__construct($name, $data, $dataName, $browser);
    }

    /**
     * Set Up to be done before Selenium tests
     * Backsup the application ini, creates modified, sets up QueryWrapper,
     * Creates test database with test data
     * Sets browser, url and speed used in tests
     */
    public function setUp()
    {
        Lib_RegistryModifier::backupApplicationINI();
        Lib_RegistryModifier::modifyApplicationINI(
            array(
                'DB_USER = sweatshop' => 'DB_USER = root',
                'DB_PASS = sweatshop' => 'DB_PASS = ',
                'DB_NAME = sweatshop' => 'DB_NAME = test_sweatshop',
                'sweatshop_system.log' => 'sweatshop_system_test.log',
            )
        );
        Lib_QueryWrapper::setUp('root', null, 'test_sweatshop');
        Lib_QueryWrapper::createTestDatabase();
        Lib_QueryWrapper::initializeTestData();
        Core_Registry::reset();

		$this->setBrowser(self::$browser);
		$this->setBrowserUrl(self::$browserUrl);
        $this->setSpeed(self::$testSpeed);
    }

    /**
     * Tears test down, rolling back every change made in setup/tests
     */
    public function teardown()
    {
        Lib_QueryWrapper::dropTestDatabase();
        Lib_RegistryModifier::returnOriginalApplicationINI();
        Core_Registry::reset();
    }

    /**
     * Open page and login
     */
    public function login()
    {
        $this->open("/sweatshop/");
        $this->verifyTextsPresent(array('Login', 'Username', 'Password'));
        $this->fillForm("css=div#loginWindow", array(
           'username' => 'Dude',
           'password' => 'salasana'
        ));
        $this->focusAndClick("css=button:contains('Login')");
        $this->verifyTextPresent("Welcome Dude");
        return $this;
    }

    /**
     * Logout and verify that sessio has ended
     */
    public function logout()
    {
        $this->focusAndClick("css=button:contains('Logout')");
        $this->verifyTextPresent('Session invalid or not started');
    }

    /**
     * Fills fields of given form
     * @param $formCss CSS locator of the form
     * @param $values List of name => value pairs to insert
     */
    protected function fillForm($formCss, $values)
    {
        foreach($values as $name => $value)
        {
            $this->type("$formCss *[name='$name']", $value);
        }
        $this->focus($formCss);
    }

    /**
     * Shorthand for focus() and click() methods
     * @param $css CSS locator of the element to click
     */
    protected function focusAndClick($css)
    {
        $this->focus($css);
        sleep(1);
        $this->click($css);
    }

    /**
     * Click a button that contains given text
     * @param String $text
     */
    protected function clickButtonThatContainsText($text ,$parent = '')
    {
        $this->focusAndClick("css=$parent button:contains('$text')");
    }

    /**
     * Verify that all given texts are present
     * @param $listOfTexts List of texts to check
     */
	protected function verifyTextsPresent($listOfTexts)
	{
		foreach($listOfTexts as $aText)
		{
			$this->verifyTextPresent($aText);
		}
	}

    /**
     * Verify that given text is present
     * Waits for text to be present and then assert it
     * @param $text Text to check
     * @throws Exception if text is not present before timeout
     */
	protected function verifyTextPresent($text)
    {
        try
		{
			$this->waitForTextPresent($text);
			$this->assertTextPresent($text);
		}
		catch(Exception $e)
		{
			throw new Exception("Text '$text' not present; ".$e->getMessage(), null, $e);
		}
    }

    /**
     * Shorthand for waitForElemenentPresent() and click() methods
     * @param $css CSS locator of the element
     */
	protected function waitForElementPresentThenClick($css)
	{
		$this->waitForElementPresent($css);
		$this->click($css);
	}


    /**
     * Waits for element to be present
     * Fails if element is not present before timeout
     * @param $css CSS locator for element
     */
	protected function waitForElementPresent($css)
	{
		$timeout = time()+self::$waitTimeout;
		while(true)
		{
			if (time() >= $timeout) $this->fail("timeout");
			try
			{
				if ($this->isElementPresent($css)) break;
			} catch (Exception $e) { }
			usleep(self::$waitSleep);
		}
	}

    /**
     * Waits for element NOT to be present
     * Fails if element is present after timeout
     * @param $css CSS locator for element 
     */
    protected function waitForElementNotPresent($css)
	{
		$timeout = time()+self::$waitTimeout;
		while(true)
		{
			if (time() >= $timeout) $this->fail("timeout");
			try
			{
				if (!$this->isElementPresent($css)) break;
			} catch (Exception $e) { }
			usleep(self::$waitSleep);
		}
	}

    /**
     * Waits for text to be present
     * Fails if text is not present before timeout
     * @param $text Text to wait for
     */
	protected function waitForTextPresent($text)
	{
		$timeout = time()+self::$waitTimeout;
		while(true)
		{
			if (time() >= $timeout) $this->fail("timeout");
			try
			{
				if ($this->isTextPresent($text)) break;
			} catch (Exception $e) { }
            usleep(self::$waitSleep);
		}
	}

    /**
     * Waits for text to NOT be present
     * Fails if text is present after timeout
     * @param $text Text to check for
     */
	protected function waitForTextNotPresent($text)
	{
		$timeout = time()+self::$waitTimeout;
		while(true)
		{
			if (time() >= $timeout) $this->fail("timeout");
			try
			{
				if (!$this->isTextPresent($text)) break;
			} catch (Exception $e) { }
			usleep(self::$waitSleep);
		}
	}

    /**
     * Clicks a cell in Ext JS grid-panel
     *
     * @param $gridCss CSS locator of gridPanel
     * @param $rowNumber Row 1 - n
     * @param $columnNumber Column 1 - n
     */
    protected function clickExtGridCell($gridCss, $rowNumber = 1, $columnNumber = 1)
    {
        $col = $columnNumber-1;
        $css = "$gridCss div.x-grid3-row:nth-child($rowNumber) table.x-grid3-row-table div.x-grid3-col-$col";
        $this->mouseDown($css);
        $this->mouseUp($css);
    }

    /**
     * Double-clicks a cell in Ext JS grid-panel
     * 
     * @param $gridCss CSS locator of gridPanel
     * @param $rowNumber Row 1 - n
     * @param $columnNumber Column 1 - n
     */
    protected function doubleClickExtGridCell($gridCss, $rowNumber = 1, $columnNumber = 1)
    {
        $col = $columnNumber-1;
        $css = "$gridCss div.x-grid3-row:nth-child($rowNumber) table.x-grid3-row-table div.x-grid3-col-$col";
        $this->clickExtGridCell($gridCss, $rowNumber, $columnNumber);
        $this->doubleClick($css);
    }


    /**
     * Fills fields in Ext JS EditorGridPanel on given row
     * $columnValues must be given in format:
     *  array(columnNumber => valueToInsert, ...)
     * @param $gridCss CSS locator of gridPanel
     * @param $rowNumber Row 1 - n
     * @param $columnValue List of column-number => value pairs
     */
    protected function fillExtEditorGridForm($gridCss, $rowNumber, $columnValues)
    {
        foreach($columnValues as $columnNumber => $value)
        {
            $this->doubleClickExtGridCell($gridCss,$rowNumber,$columnNumber);
            $this->type("css=input.x-form-focus", $value);
        }
    }

    /**
     * Click on Ext JS FieldSet's toggle-button
     * @param $legendText The legend-text to identify fieldset with
     */
    protected function toggleExtFieldset($legendText)
    {
        $this->click("css=legend:contains('$legendText') div.x-tool-toggle");
    }
}
